package com.wanrue.tcc.admin.etl;

import com.wanrue.tcc.admin.entity.Transaction;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.scripting.support.StaticScriptSource;

import java.util.*;

public class RedisImport implements DataImport {

    private String name;

    private String prefix = "TCC:";

    private RedisTemplate<String, String> redisTemplate;

    public RedisImport(String name, RedisConnectionFactory connectionFactory) {
        this.name = name;
        this.redisTemplate = new RedisTemplate();
        redisTemplate.setConnectionFactory(connectionFactory);
        RedisSerializer<String> keyserializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(keyserializer);
        redisTemplate.setHashKeySerializer(keyserializer);
        redisTemplate.afterPropertiesSet();
    }

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public List<Transaction> loadAll() {
        List<String> strings = Arrays.asList("GLOBAL_TX_ID", "BRANCH_QUALIFIER", "DOMAIN", "PARTNER_STR", "STATUS", "TYPE", "COUNT", "VERSION", "RESULT", "CREATE_TIME", "UPDATE_TIME");
        List<Transaction> list = new ArrayList<>();
        Set<String> keys = redisTemplate.keys(prefix + "*");
        HashOperations hashOp = redisTemplate.opsForHash();
        for (String key : keys) {
            list.add(ListToTransaction(hashOp.multiGet(key, strings)));
        }
        return list;
    }

    @Override
    public int update(Transaction t) {
        String key = prefix + t.getDomain() + ":" + t.getGlobalId() + ":" + t.getBranchId();
        Long res = 0L;
        try {
            String k2 = key.split(":")[2];
            String lua = "if redis.call('HGET', KEYS[1], 'VERSION') == ARGV[1] then redis.call('HMSET', KEYS[1], 'VERSION', ARGV[2], 'STATUS', ARGV[3], 'RESULT', ARGV[4],'COUNT', ARGV[5],'UPDATE_TIME', ARGV[6]) if %s then local keys = redis.call('keys', KEYS[2]) for i, v in ipairs(keys) do redis.call('HMSET', v, 'MSTATUS',ARGV[3], 'MRESULT', ARGV[4]) end return 2 end  if %s then redis.call('DEL', KEYS[1]) end return 1 end return 0 ";
            if (t.getResult() != 0 && t.getType() == 1) {
                lua = String.format(lua, "true", "false");
            } else {
                lua = String.format(lua, "false", "false");
            }
            DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
            redisScript.setScriptSource(new StaticScriptSource(lua));
            redisScript.setResultType(Long.class);
            res = redisTemplate.execute(redisScript, Arrays.asList(key, k2), t.getVersion(), t.getVersion() + 1, t.getStatus(), t.getResult(), t.getCount(), new Date());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Integer.parseInt(res + "");
    }

    private Transaction ListToTransaction(List list) {
        Transaction t = new Transaction();
        String global_tx_id = (String) list.get(0);
        t.setGlobalId(global_tx_id);
        String branch_qualifier = (String) list.get(1);
        t.setBranchId(branch_qualifier);
        String domain = (String) list.get(2);
        t.setDomain(domain);
        String partner_str = (String) list.get(3);
        t.setPartner(partner_str);
        Integer status = (Integer) list.get(4);
        t.setStatus(status);
        Integer type = (Integer) list.get(5);
        t.setType(type);
        Integer count = (Integer) list.get(6);
        t.setCount(count);
        Integer version = (Integer) list.get(7);
        t.setVersion(version);
        Integer result = (Integer) list.get(8);
        t.setResult(result);
        Date create_time = (Date) list.get(9);
        t.setCreateTime(create_time);
        Date update_time = (Date) list.get(10);
        t.setUpdateTime(update_time);
        return t;
    }

    @Override
    public void destroy() {
    }
}